#include "string-to-function.h"

using namespace stf;

/* Методы Function
————————————————————————————————————————————————————————————

————————————————————————————————————————————————————————————
*/

/* Set
------------------------------------------------------------
s_ptr — необзятельный параметр, служит для передачи в 
функцию указатель на объект (замена this для статических 
членов класса). По умолчанию равен nullptr.
------------------------------------------------------------
*/
void Function::Set( const std::string& name, const int& name_words_count, void* f_ptr, void* s_ptr ) {
	func_ptr = f_ptr;
	object_ptr = s_ptr;
	
	if( s_ptr == nullptr )
		pass_object_ptr = 0;
	else
		pass_object_ptr = 1;

	name_tokens_count = name_words_count;
}

int Function::CheckArguments( const std::vector<Token*>& tokens, const std::string pattern ) {
	if( tokens.size() != pattern.size() + name_tokens_count )
		return 0;

	for( int i = 0; i < pattern.size(); i++ )
		if( !TypesAreCompatible( pattern[i], *tokens[i+name_tokens_count]) )
			return 0;

	return 1;
}

/* TypesAreCompatible
------------------------------------------------------------
Правила совместимости.
Тип используемый в функции : Тип токена:
string : все;
float  : float и int;
int    : int;
char   : один любой символ.
------------------------------------------------------------
*/
int Function::TypesAreCompatible( const char& arg_type, const Token& token ) {
	Token_Type token_type = token.type;

	if( arg_type == IP_TOKEN_WORD )
		return 1;

	if( arg_type == token_type )
		return 1;

	if( arg_type == IP_TOKEN_FLOAT && token_type == IP_TOKEN_INT )
		return 1;
	
	if( arg_type == IP_TOKEN_CHAR && token.value.size() == 1 ) 
		return 1;

	return 0;
}

int Function::NameWordsCount() {
	return name_tokens_count;
}

/* Шаблоны функций
————————————————————————————————————————————————————————————
Вторым параметром CheckArguments должен быть передан шаблон
описывающий количество и типы аргументов.

Обозначения используемые шаблонах:
i — int; 
f — float;
c — char;
s — string;
————————————————————————————————————————————————————————————
*/
void NoArgs::Call( const std::vector<Token*>& tokens ) {
	if( !CheckArguments( tokens, "" ) )
		return ;
	
	if( !pass_object_ptr ) 
	{
		void (*func)() = static_cast<void (*)()>(func_ptr);
		func();
	}
	else
	{
		void (*func)(void*) = static_cast<void (*)(void*)>(func_ptr);
		func( object_ptr );
	}
}

void I::Call( const std::vector<Token*>& tokens ) {
	if( !CheckArguments( tokens, "i" ) )
		return ;
	
	int arg1 = atoi( tokens[name_tokens_count]->value.c_str() );

	if( !pass_object_ptr )
	{
		void (*func)(int) = static_cast<void (*)(int)>(func_ptr);
		func( arg1 );
	}
	else
	{
		void (*func)(int, void*) = static_cast<void (*)(int, void*)>(func_ptr);
		func( arg1, object_ptr );
	}
}

void F::Call( const std::vector<Token*>& tokens ) {
	if( !CheckArguments( tokens, "f" ) )
		return ;
	
	float arg1 = atof( tokens[name_tokens_count]->value.c_str() );

	if( !pass_object_ptr )
	{
		void (*func)(float) = static_cast<void (*)(float)>(func_ptr);
		func( arg1 );
	}
	else
	{
		void (*func)(float, void*) = static_cast<void (*)(float, void*)>(func_ptr);
		func( arg1, object_ptr );
	}
}

void S::Call( const std::vector<Token*>& tokens ) {
	if( !CheckArguments( tokens, "s" ) )
		return ;
	
	std::string& arg1 = tokens[name_tokens_count]->value;

	if( !pass_object_ptr )
	{
		void (*func)(std::string) = static_cast<void (*)(std::string)>(func_ptr);
		func( arg1 );
	}
	else
	{
		void (*func)(std::string, void*) = static_cast<void (*)(std::string, void*)>(func_ptr);
		func( arg1, object_ptr );
	}
}

void C::Call( const std::vector<Token*>& tokens ) {
	if( !CheckArguments( tokens, "c" ) )
		return ;
	
	char arg1 = tokens[name_tokens_count]->value[0];

	if( !pass_object_ptr )
	{
		void (*func)(char) = static_cast<void (*)(char)>(func_ptr);
		func( arg1 );
	}
	else
	{
		void (*func)(char, void*) = static_cast<void (*)(char, void*)>(func_ptr);
		func( arg1, object_ptr );
	}
}

void FFF::Call( const std::vector<Token*>& tokens ) {
	if( !CheckArguments( tokens, "fff" ) )
		return ;
	
	float arg1 = atof( tokens[name_tokens_count]->value.c_str() );
	float arg2 = atof( tokens[name_tokens_count+1]->value.c_str() );
	float arg3 = atof( tokens[name_tokens_count+2]->value.c_str() );

	if( !pass_object_ptr )
	{
		void (*func)(float, float, float) = static_cast<void (*)(float, float, float)>(func_ptr);
		func( arg1, arg2, arg3 );
	}
	else
	{
		void (*func)(float, float, float, void*) = static_cast<void (*)(float, float, float, void*)>(func_ptr);
		func( arg1, arg2, arg3, object_ptr );
	}
}

void IS::Call( const std::vector<Token*>& tokens ) {
	if( !CheckArguments( tokens, "is" ) )
		return ;
	
	int			arg1 = atoi( tokens[name_tokens_count]->value.c_str() );
	std::string arg2 = tokens[name_tokens_count+1]->value.c_str();

	if( !pass_object_ptr )
	{
		void (*func)(int, std::string) = static_cast<void (*)(int, std::string)>(func_ptr);
		func( arg1, arg2 );
	}
	else
	{
		void (*func)(int, std::string, void*) = static_cast<void (*)(int, std::string, void*)>(func_ptr);
		func( arg1, arg2, object_ptr );
	}
}

void SI::Call( const std::vector<Token*>& tokens ) {
	if( !CheckArguments( tokens, "si" ) )
		return ;
	
	std::string arg1 = tokens[name_tokens_count]->value.c_str();
	int			arg2 = atoi( tokens[name_tokens_count+1]->value.c_str() );

	if( !pass_object_ptr )
	{
		void (*func)(std::string,int) = static_cast<void (*)(std::string,int)>(func_ptr);
		func( arg1, arg2 );
	}
	else
	{
		void (*func)(std::string,int, void*) = static_cast<void (*)(std::string,int, void*)>(func_ptr);
		func( arg1, arg2, object_ptr );
	}
}

/* String-to-function
————————————————————————————————————————————————————————————

————————————————————————————————————————————————————————————
*/
String_To_Function::String_To_Function() {
	for( int i = 0; i < 256; i++ )
		symbols[i] = IP_SYMBOL_CHAR;

	for( int i = '0'; i <= '9'; i++ )
		symbols[i] = IP_SYMBOL_NUMBER;

	symbols['!'] = IP_SYMBOL_CHAR;
	symbols['@'] = IP_SYMBOL_CHAR;
	symbols['#'] = IP_SYMBOL_CHAR;
	symbols['$'] = IP_SYMBOL_CHAR;
	symbols['%'] = IP_SYMBOL_CHAR;
	symbols['^'] = IP_SYMBOL_CHAR;
	symbols['&'] = IP_SYMBOL_CHAR;
	symbols['*'] = IP_SYMBOL_CHAR;
	symbols['('] = IP_SYMBOL_CHAR;
	symbols[')'] = IP_SYMBOL_CHAR;
	symbols['_'] = IP_SYMBOL_CHAR;
	symbols['-'] = IP_SYMBOL_CHAR;
	symbols['+'] = IP_SYMBOL_CHAR;
	symbols['='] = IP_SYMBOL_CHAR;
	symbols['['] = IP_SYMBOL_CHAR;
	symbols[']'] = IP_SYMBOL_CHAR;
	symbols['{'] = IP_SYMBOL_CHAR;
	symbols['}'] = IP_SYMBOL_CHAR;
	symbols['"'] = IP_SYMBOL_CHAR;
	symbols['|'] = IP_SYMBOL_CHAR;
	symbols['/'] = IP_SYMBOL_CHAR;
	symbols[' '] = IP_SYMBOL_BREAKER;
	symbols[','] = IP_SYMBOL_CHAR;
	symbols['.'] = IP_SYMBOL_FLOAT_DIVIDER;
}

int String_To_Function::Update( const std::string name, void* func_ptr, void* object_ptr ) {
	int name_hash = H( name );

	if( !name.size() || func_ptr == nullptr )
		return 0;

	std::map<int, Function*>::iterator it = functions.find( name_hash );

	if( it == functions.end() )
		return 0;

	int name_words_count = it->second->NameWordsCount();

	it->second->Set( name, name_words_count, func_ptr, object_ptr );
}

void String_To_Function::Remove( const std::string name ) {
	std::map<int, Function*>::iterator it;
	
	int name_hash = H( name );

	it = functions.find( name_hash );

	if( it != functions.end() )
		functions.erase( it );
}

int String_To_Function::Call( const std::string line ) {
	if( !line.size() )
		return 0;

	std::vector<Token*> tokens;
	
	TokenizeString( line, tokens );
	Function* function = FindFunction( tokens );

	if( function == nullptr )
	{
		ClearTokens( tokens );
		return 0;
	}
		
	function->Call( tokens );
	ClearTokens( tokens );

	return 1;
}

void String_To_Function::SetSymbolType( const char symbol, const Symbol_Type new_type ) {
	if( new_type >= 0 && new_type <= IP_SYMBOL_BREAKER ) 
		symbols[symbol] = new_type;
}

Symbol_Type String_To_Function::GetSymbolType( const char symbol ) {
	return symbols[symbol];
}

int String_To_Function::CountWords( const std::string& line ) {
	int words_count = 1;
	int line_size = line.size();

	if( !line_size )
		return 0;

	for( int i = 0; i < line_size; i++ )
		if( symbols[line[i]] == IP_SYMBOL_BREAKER && i != line_size - 1 ) 
			words_count++;

	return words_count;
}

void String_To_Function::TokenizeString( const std::string& line, std::vector<Token*>& tokens ) {
	int line_size = line.size();
	std::string tmp_string = "";

	for( int i = 0; i < line_size; i++ )
	{
		if( symbols[line[i]] != IP_SYMBOL_BREAKER )
			tmp_string.push_back( line[i] );
		else if( tmp_string.size() )
		{
			tokens.push_back( CreateToken( tmp_string ) );
			tmp_string.clear();
		}
	}

	if( tmp_string.size() )
		tokens.push_back( CreateToken( tmp_string ) );
}

Token* String_To_Function::CreateToken( const std::string& line ) {
	Token* new_token = new Token;

	new_token->type = GetTokenType( line );
	new_token->value = line;

	return new_token;
}

/* 
------------------------------------------------------------
start — индекс символа в строке с которого начинается
проверка. После проверки на знак - в нулевом символе
------------------------------------------------------------
*/
Token_Type String_To_Function::GetTokenType( const std::string& line ) {
	int token_type = IP_TOKEN_WORD;
	int float_dividers_count = 0;
	int line_size = line.size();
	int start = ( line[0] == '-' && line_size > 1 );
	
	for( int i = start; i < line_size; i++ )
		if( symbols[line[i]] == IP_SYMBOL_CHAR )
			return IP_TOKEN_WORD;
		else if( symbols[line[i]] == IP_SYMBOL_FLOAT_DIVIDER )
			float_dividers_count++;

	switch( float_dividers_count )
	{
	case 0:
		return IP_TOKEN_INT;
		break;
	case 1:
		return IP_TOKEN_FLOAT;
		break;
	default:
		return IP_TOKEN_WORD;
		break;
	}
}

Function* String_To_Function::FindFunction( const std::vector<Token*>& tokens ) {
	int name_hash;
	
	int tokens_size = tokens.size();
	std::string command_name;

	std::map<int, Function*>::iterator it;

	for( int i = 0; i < tokens_size; i++ )
	{
		command_name.append( tokens[i]->value );
		name_hash = H( command_name );

		it = functions.find( name_hash );
		if( it != functions.end() )
			return it->second;

		command_name.append(" ");
	}

	return nullptr;
}

void String_To_Function::ClearTokens( std::vector<Token*>& list ) {
	int list_size = list.size();
	
	if( list_size == 0 ) 
		return ;

	for( int i = 0; i < list_size; i++ )
		delete list[i];

	list.clear();
}